1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-19 13:19:44 +02:00

feat: Languages with country codes

This commit is contained in:
Maksim Eltyshev 2024-07-21 19:33:57 +02:00
parent 79ad1836a8
commit 07e1903bb5
83 changed files with 211 additions and 99 deletions

View file

@ -12,7 +12,7 @@
"dequal": "^2.0.3",
"easymde": "^2.18.0",
"history": "^5.3.0",
"i18next": "^23.11.5",
"i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0",
"initials": "^3.1.2",
"js-cookie": "^3.0.5",
@ -30,7 +30,7 @@
"react-datepicker": "^4.25.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-i18next": "^14.1.2",
"react-i18next": "^15.0.0",
"react-input-mask": "^2.0.4",
"react-markdown": "^8.0.7",
"react-photoswipe-gallery": "^2.2.7",
@ -2007,9 +2007,9 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
},
"node_modules/@babel/runtime": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz",
"integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
"integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@ -11975,9 +11975,9 @@
}
},
"node_modules/i18next": {
"version": "23.11.5",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz",
"integrity": "sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==",
"version": "23.12.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.2.tgz",
"integrity": "sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg==",
"funding": [
{
"type": "individual",
@ -22084,11 +22084,11 @@
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/react-i18next": {
"version": "14.1.2",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.2.tgz",
"integrity": "sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==",
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.0.tgz",
"integrity": "sha512-2O3IgF4zivg57Q6p6i+ChDgJ371IDcEWbuWC6gvoh5NbkDMs0Q+O7RPr4v61+Se32E0V+LmtwePAeqWZW0bi6g==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"@babel/runtime": "^7.24.8",
"html-parse-stringify": "^3.0.1"
},
"peerDependencies": {

View file

@ -65,7 +65,7 @@
"dequal": "^2.0.3",
"easymde": "^2.18.0",
"history": "^5.3.0",
"i18next": "^23.11.5",
"i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0",
"initials": "^3.1.2",
"js-cookie": "^3.0.5",
@ -83,7 +83,7 @@
"react-datepicker": "^4.25.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-i18next": "^14.1.2",
"react-i18next": "^15.0.0",
"react-input-mask": "^2.0.4",
"react-markdown": "^8.0.7",
"react-photoswipe-gallery": "^2.2.7",

View file

@ -58,9 +58,9 @@ i18n
.use(initReactI18next)
.init({
resources: embeddedLocales,
fallbackLng: 'en',
fallbackLng: 'en-US',
supportedLngs: languages,
load: 'languageOnly',
load: 'currentOnly',
interpolation: {
escapeValue: false,
format(value, format, language) {
@ -80,7 +80,7 @@ i18n
});
i18n.loadCoreLocale = async (language = i18n.resolvedLanguage) => {
if (language === 'en') {
if (language === i18n.options.fallbackLng[0]) {
return;
}

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'bg',
language: 'bg-BG',
country: 'bg',
name: 'Български',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'cs',
language: 'cs-CZ',
country: 'cz',
name: 'Čeština',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'da',
language: 'da-DK',
country: 'dk',
name: 'Dansk',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'de',
language: 'de-DE',
country: 'de',
name: 'Deutsch',
embeddedLocale: login,

View file

@ -4,8 +4,8 @@ import login from './login';
import core from './core';
export default {
language: 'en',
country: 'gb',
language: 'en-US',
country: 'us',
name: 'English',
embeddedLocale: merge(login, core),
};

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'es',
language: 'es-ES',
country: 'es',
name: 'Español',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'fa',
language: 'fa-IR',
country: 'ir',
name: 'فارسی',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'fr',
language: 'fr-FR',
country: 'fr',
name: 'Français',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'hu',
language: 'hu-HU',
country: 'hu',
name: 'Magyar',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'id',
language: 'id-ID',
country: 'id',
name: 'Bahasa Indonesia',
embeddedLocale: login,

View file

@ -1,53 +1,53 @@
import bg from './bg';
import cs from './cs';
import da from './da';
import de from './de';
import en from './en';
import es from './es';
import fa from './fa';
import fr from './fr';
import hu from './hu';
import id from './id';
import it from './it';
import ja from './ja';
import ko from './ko';
import nl from './nl';
import pl from './pl';
import pt from './pt';
import ro from './ro';
import ru from './ru';
import sk from './sk';
import sv from './sv';
import tr from './tr';
import ua from './ua';
import uz from './uz';
import zh from './zh';
import bgBG from './bg-BG';
import csCZ from './cs-CZ';
import daDK from './da-DK';
import deDE from './de-DE';
import enUS from './en-US';
import esES from './es-ES';
import faIR from './fa-IR';
import frFR from './fr-FR';
import huHU from './hu-HU';
import idID from './id-ID';
import itIT from './it-IT';
import jaJP from './ja-JP';
import koKR from './ko-KR';
import nlNL from './nl-NL';
import plPL from './pl-PL';
import ptBR from './pt-BR';
import roRO from './ro-RO';
import ruRU from './ru-RU';
import skSK from './sk-SK';
import svSE from './sv-SE';
import trTR from './tr-TR';
import ukUA from './uk-UA';
import uzUZ from './uz-UZ';
import zhCN from './zh-CN';
const locales = [
bg,
cs,
da,
de,
en,
es,
fa,
fr,
hu,
id,
it,
ja,
ko,
nl,
pl,
pt,
ro,
ru,
sk,
sv,
tr,
ua,
uz,
zh,
bgBG,
csCZ,
daDK,
deDE,
enUS,
esES,
faIR,
frFR,
huHU,
idID,
itIT,
jaJP,
koKR,
nlNL,
plPL,
ptBR,
roRO,
ruRU,
skSK,
svSE,
trTR,
ukUA,
uzUZ,
zhCN,
];
export default locales;

View file

@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/it';
export default {
dateFns,
format: {
date: 'd/M/yyyy',
time: 'p',

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'it',
language: 'it-IT',
country: 'it',
name: 'Italiano',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'ja',
language: 'ja-JP',
country: 'jp',
name: '日本語',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'ko',
language: 'ko-KR',
country: 'kr',
name: '한국어',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'nl',
language: 'nl-NL',
country: 'nl',
name: 'Nederlands',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'pl',
language: 'pl-PL',
country: 'pl',
name: 'Polski',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'pt',
language: 'pt-BR',
country: 'br',
name: 'Português',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'ro',
language: 'ro-RO',
country: 'ro',
name: 'Română',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'ru',
language: 'ru-RU',
country: 'ru',
name: 'Русский',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'sk',
language: 'sk-SK',
country: 'sk',
name: 'Slovenčina',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'sv',
language: 'sv-SE',
country: 'se',
name: 'Svenska',
embeddedLocale: login,

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'tr',
language: 'tr-TR',
country: 'tr',
name: 'Türkçe',
embeddedLocale: login,

View file

@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/uk';
export default {
dateFns,
format: {
date: 'd/M/yyyy',
time: 'p',

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'ua',
language: 'uk-UA',
country: 'ua',
name: 'Українська',
embeddedLocale: login,

View file

@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/uz';
export default {
dateFns,
format: {
date: 'M/d/yyyy',
time: 'p',

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'uz',
language: 'uz-UZ',
country: 'uz',
name: "O'zbek",
embeddedLocale: login,

View file

@ -1,4 +1,8 @@
import dateFns from 'date-fns/locale/zh-CN';
export default {
dateFns,
format: {
date: 'M/d/yyyy',
time: 'p',

View file

@ -1,7 +1,7 @@
import login from './login';
export default {
language: 'zh',
language: 'zh-CN',
country: 'cn',
name: '中文',
embeddedLocale: login,

View file

@ -50,7 +50,7 @@ module.exports = {
},
language: {
type: 'string',
isNotEmptyString: true,
isIn: User.LANGUAGES,
allowNull: true,
},
subscribeToOwnCards: {

View file

@ -36,7 +36,7 @@ module.exports = {
},
language: {
type: 'string',
isNotEmptyString: true,
isIn: User.LANGUAGES,
allowNull: true,
},
subscribeToOwnCards: {

View file

@ -5,11 +5,39 @@
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
*/
const LANGUAGES = [
'bg-BG',
'cs-CZ',
'da-DK',
'de-DE',
'en-US',
'es-ES',
'fa-IR',
'fr-FR',
'hu-HU',
'id-ID',
'it-IT',
'ja-JP',
'ko-KR',
'nl-NL',
'pl-PL',
'pt-BR',
'ro-RO',
'ru-RU',
'sk-SK',
'sv-SE',
'tr-TR',
'uk-UA',
'uz-UZ',
'zh-CN',
];
const OIDC = {
id: '_oidc',
};
module.exports = {
LANGUAGES,
OIDC,
attributes: {
@ -62,7 +90,7 @@ module.exports = {
},
language: {
type: 'string',
isNotEmptyString: true,
isIn: LANGUAGES,
allowNull: true,
},
subscribeToOwnCards: {

View file

@ -15,7 +15,7 @@ module.exports.up = async (knex) => {
const attachments = await knex('attachment');
// eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) {
for (const attachment of attachments) {
if (attachment.is_image) {
const image = sharp(
path.join(config.custom.attachmentsPath, attachment.dirname, attachment.filename),
@ -54,7 +54,7 @@ module.exports.down = async (knex) => {
const attachments = await knex('attachment');
// eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) {
for (const attachment of attachments) {
// eslint-disable-next-line no-await-in-loop
await knex('attachment')
.update({

View file

@ -93,7 +93,7 @@ module.exports.up = async (knex) => {
const attachments = await knex('attachment').whereNotNull('image');
// eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) {
for (const attachment of attachments) {
// eslint-disable-next-line no-await-in-loop
const image = await processAttachmentImage(attachment, config.custom.attachmentsPath);

View file

@ -113,7 +113,7 @@ module.exports.up = async (knex) => {
const users = await knex('user_account').whereNotNull('avatar');
// eslint-disable-next-line no-restricted-syntax
for (user of users) {
for (const user of users) {
// eslint-disable-next-line no-await-in-loop
await processUserAvatar(user, config.custom.userAvatarsPath);
}
@ -121,7 +121,7 @@ module.exports.up = async (knex) => {
const projects = await knex('project').whereNotNull('background_image');
// eslint-disable-next-line no-restricted-syntax
for (project of projects) {
for (const project of projects) {
// eslint-disable-next-line no-await-in-loop
await processProjectBackgroundImage(project, config.custom.projectBackgroundImagesPath);
}
@ -129,7 +129,7 @@ module.exports.up = async (knex) => {
const attachments = await knex('attachment').whereNotNull('image');
// eslint-disable-next-line no-restricted-syntax
for (attachment of attachments) {
for (const attachment of attachments) {
// eslint-disable-next-line no-await-in-loop
await processAttachmentImage(attachment, config.custom.attachmentsPath);
}

View file

@ -0,0 +1,68 @@
const _ = require('lodash');
const LANGUAGES = [
'bg-BG',
'cs-CZ',
'da-DK',
'de-DE',
'en-US',
'es-ES',
'fa-IR',
'fr-FR',
'hu-HU',
'id-ID',
'it-IT',
'ja-JP',
'ko-KR',
'nl-NL',
'pl-PL',
'pt-BR',
'ro-RO',
'ru-RU',
'sk-SK',
'sv-SE',
'tr-TR',
'uz-UZ',
'zh-CN',
];
const LANGUAGE_BY_PREV_LANGUAGE = LANGUAGES.reduce(
(result, language) => ({
...result,
[language.split('-')[0]]: language,
}),
{},
);
LANGUAGE_BY_PREV_LANGUAGE.ua = 'uk-UA';
const PREV_LANGUAGE_BY_LANGUAGE = _.invert(LANGUAGE_BY_PREV_LANGUAGE);
module.exports.up = async (knex) => {
const users = await knex('user_account').whereNotNull('language');
const prevLanguages = [...new Set(users.map((user) => user.language))];
// eslint-disable-next-line no-restricted-syntax
for (const prevLanguage of prevLanguages) {
// eslint-disable-next-line no-await-in-loop
await knex('user_account')
.update({
language: LANGUAGE_BY_PREV_LANGUAGE[prevLanguage],
})
.where('language', prevLanguage);
}
};
module.exports.down = async (knex) => {
const users = await knex('user_account').whereNotNull('language');
const languages = [...new Set(users.map((user) => user.language))];
// eslint-disable-next-line no-restricted-syntax
for (const language of languages) {
// eslint-disable-next-line no-await-in-loop
await knex('user_account')
.update({
language: PREV_LANGUAGE_BY_LANGUAGE[language],
})
.where('language', language);
}
};