mirror of
https://github.com/Afilmory/afilmory
synced 2026-04-24 23:05:05 +00:00
- Added i18next and react-i18next for multi-language support in the application. - Created localization files for English, Japanese, Korean, Traditional Chinese, and Simplified Chinese. - Implemented translation hooks in various components to replace hardcoded strings with translatable keys. - Updated ESLint configuration to include new i18n JSON validation rules. - Introduced a new event bus for handling i18n updates during development. Signed-off-by: Innei <tukon479@gmail.com>
141 lines
3.9 KiB
JavaScript
141 lines
3.9 KiB
JavaScript
// @ts-check
|
|
/** @type {import("eslint").ESLint.Plugin} */
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
import { cleanJsonText } from './utils.js'
|
|
|
|
export default {
|
|
rules: {
|
|
'valid-i18n-keys': {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description:
|
|
'Ensure i18n JSON keys are flat and valid as object paths',
|
|
category: 'Possible Errors',
|
|
recommended: true,
|
|
},
|
|
fixable: null,
|
|
},
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
const { filename, sourceCode } = context
|
|
|
|
if (!filename.endsWith('.json')) return
|
|
|
|
let json
|
|
try {
|
|
json = JSON.parse(cleanJsonText(sourceCode.text))
|
|
} catch {
|
|
context.report({
|
|
node,
|
|
message: 'Invalid JSON format',
|
|
})
|
|
return
|
|
}
|
|
|
|
const keys = Object.keys(json)
|
|
const keyPrefixes = new Set()
|
|
|
|
for (const key of keys) {
|
|
if (key.includes('.')) {
|
|
const parts = key.split('.')
|
|
for (let i = 1; i < parts.length; i++) {
|
|
const prefix = parts.slice(0, i).join('.')
|
|
if (keys.includes(prefix)) {
|
|
context.report({
|
|
node,
|
|
message: `Invalid key structure: '${key}' conflicts with '${prefix}'`,
|
|
})
|
|
}
|
|
keyPrefixes.add(prefix)
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const key of keys) {
|
|
if (keyPrefixes.has(key)) {
|
|
context.report({
|
|
node,
|
|
message: `Invalid key structure: '${key}' is a prefix of another key`,
|
|
})
|
|
}
|
|
}
|
|
},
|
|
}
|
|
},
|
|
},
|
|
'no-extra-keys': {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description:
|
|
"Ensure non-English JSON files don't have extra keys not present in en.json",
|
|
category: 'Possible Errors',
|
|
recommended: true,
|
|
},
|
|
fixable: 'code', // Set fixable to "code"
|
|
},
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
const { filename, sourceCode } = context
|
|
|
|
if (!filename.endsWith('.json')) return
|
|
|
|
const parts = filename.split(path.sep)
|
|
const lang = parts.at(-1).split('.')[0]
|
|
const namespace = parts.at(-2)
|
|
|
|
if (lang === 'en') return
|
|
|
|
let currentJson = {}
|
|
let englishJson = {}
|
|
|
|
try {
|
|
currentJson = JSON.parse(sourceCode.text)
|
|
const englishFilePath = path.join(
|
|
path.dirname(filename),
|
|
'../',
|
|
namespace,
|
|
'en.json',
|
|
)
|
|
englishJson = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'))
|
|
} catch (error) {
|
|
context.report({
|
|
node,
|
|
message: `Error parsing JSON: ${error.message}`,
|
|
})
|
|
return
|
|
}
|
|
|
|
const extraKeys = Object.keys(currentJson).filter(
|
|
(key) => !Object.prototype.hasOwnProperty.call(englishJson, key),
|
|
)
|
|
|
|
for (const key of extraKeys) {
|
|
context.report({
|
|
node,
|
|
message: `Key "${key}" is present in ${lang}.json but not in en.json for namespace "${namespace}"`,
|
|
fix(fixer) {
|
|
const newJson = Object.fromEntries(
|
|
Object.entries(currentJson).filter(
|
|
([k]) => !extraKeys.includes(k),
|
|
),
|
|
)
|
|
|
|
const newText = `${JSON.stringify(newJson, null, 2)}\n`
|
|
|
|
return fixer.replaceText(node, newText)
|
|
},
|
|
})
|
|
}
|
|
},
|
|
}
|
|
},
|
|
},
|
|
},
|
|
}
|